﻿////@alex:注意，文件的编码最好为utf-8 无bom格式，这样会兼容win32/mingw/g++以及cl.exe，而单纯的utf-8有bom格式的话，cl.exe可以支持，但是win32/mingw/g++是会出问题的，而且在cygwin下，也是不支持utf-8有bom格式的。所以，为了兼容，使用utf-8无bom格式为好
//如果调整成utf-8 有bom格式的话, vs不会提示出warning c4819的, 但是考虑到兼容性问题, 还是用无bom格式的. 可以用notpad++调整格式

#include <iostream>

#define main testmain_tmp_var_and_call_func_stack

class rect
{
	private:
		int x,y,w,h;
	public:
		rect() { x=y=w=h=0;};///@alex: default ctor,包括无参数构造函数以及参数均有默认值的构造函数
		rect(int x_, int y_, int w_, int h_) {x=x_; y=y_; w=w_; h=h_;};
		~rect() {};///@alex: 析构函数容易被忘记
		rect(const rect & copy) {x=copy.x; y=copy.y; w=copy.w; h=copy.h;};
		rect & operator= (const rect & assign) {x=assign.x; y=assign.y; w=assign.w; h=assign.h; return *this;};///@alex: 参考ecpp#10,ecpp#11，将赋值型的操作返回为引用 ///@alex:这边的return *this；也很容易被忘记
		rect & operator+= (const rect & rhs){ x+=rhs.x; y+=rhs.y; w+=rhs.w; h+=rhs.h; return *this;};///@alex:ecpp#10
		rect & operator-= (const rect & rhs){ x-=rhs.x; y-=rhs.y; w-=rhs.w; h-=rhs.h; return *this;};///@alex:ecpp#10
		rect & operator*= (const rect & rhs){ x*=rhs.x; y*=rhs.y; w*=rhs.w; h*=rhs.h; return *this;};
		rect & operator/= (const rect & rhs){ x/=rhs.x; y/=rhs.y; w/=rhs.w; h/=rhs.h; return *this;};///@alex:如果这个return *this丢了，代码还是可以编译通过，但是运行时会出错，因为operator+会使用return tmp+=rhs;而这个时候，却没有+=的返回值，自然出错了

		int & operator[](int index) { if(index==0) return x; else if(index==1) return y; else if(index==2) return w; else if(index=3) return h; else throw;/* ///todo@alex: 错误处理，可以使用自定义的coutlog或者将对应值致为一个特殊的值，例如极大值或极小值（返回特殊的值的方式称为“error return value”，那么，出了问题很难处理或者给外部开发人员获得） */ };///@alex:这样的对private可以访问么？答案是绝对可以的！！///todo@alex: throw在这儿的含义
		///todo@alex:1.bound checker;2.logger;3.data integral
		///@alex:如上的错误处理需要很好的规划才可以，例如，如果输出特殊值作为错误的返回，那么对于一些情况会出很大问题。例如，string a,b,c,d; d=a+b+c;这边如果在string结合的时候没有足够空间，而通过返回一个特殊的字符表示错误的话，明显无法达成出错处理的目的，因为这边完全就掩盖了错误的发生///@reference:http://blog.codingnow.com/2005/12/cpp_exception.html#comment-96
		int & operator()(int index) { return (*this)[index]; };

		rect & operator++() { ++x;++y;++w;++h; return *this;};///@alex: pre-fix plus plus; rect i; ++i; 
		rect operator++(int) { rect tmp(*this); ++(*this); return tmp;};///@alex: post-fix plus plus; rect i; i++;

		int getx(void) const {return x;}
		int gety(void) const {return y;}
		int getw(void) const {return w;}
		int geth(void) const {return h;}

};///@alex:最后的分号也容易被忘记

const rect operator+ (const rect & lhs, const rect &rhs) {rect tmp(lhs); return tmp+=rhs; };///@alex:ecpp#21,ecpp#20
const rect operator- (const rect & lhs, const rect &rhs) {rect tmp(lhs); return tmp-=rhs;}; 
const rect operator* (const rect & lhs, const rect &rhs) {rect tmp(lhs); return tmp*=rhs;}; 
const rect operator/ (const rect & lhs, const rect &rhs) {rect tmp(lhs); return tmp/=rhs;}; 

std::ostream & operator<<(std::ostream & cout_, const /* ///@alex: 如果去掉了这个const，在g++是编译不通过的，不知道为何，而在cl.exe 2008下是可以顺利通过编译的 */ rect & rhs) { cout_<<"x is:"<<rhs.getx()<<". y is:"<<rhs.gety()<<". w is:"<<rhs.getw()<<". h is:"<<rhs.geth()<<"."<<std::endl; return cout_; }///@alex:这便的const rect & rhs的const容易被丢失，没加const的时候，可以用getx等函数，但是加了const后，如果getx没有const修饰，是不能被调用的，因为有了const的限制
///@alex: 这边的std::ostream & operator<<(std::ostream & cout_, const rect & rhs)，如果const rect &rhs中，没有const，那么在std::cout<<d++的时候，会出现错误，但是仅限于g++，而cl是不会出现错误的，是因为cl有扩展可以兼容这种写法，可以使用/Za去掉cl的扩展，那么就会报告错误了。
///@alex:这种写法就是，d++返回为传值返回，那么会有一个临时对象存在，而函数对于一个临时对象的引用，是必须加上const的，也就是说，对于临时变量的引用，是必须加上const的！！！否则，g++编译器默认无法通过，而cl在关闭对c++的扩展(/Za)后，也是无法允许的。但是cl默认开启的扩展(/Ze)是允许这种写法的。///@reference:http://topic.csdn.net/u/20090818/06/41659e77-553f-476e-9cd0-35713890fbaa.html (第四楼)
///@alex:可以参考msdn的说明///@reference:http://msdn.microsoft.com/zh-cn/library/cfbk5ddc.aspx


int main(void)
{
	rect a(10,9,8,7);
	rect b(1,2,3,4);
	rect c;
	rect d(a);
	std::cout<<"a: "<<a<<"b: "<<b<<"c: "<<c<<"d: "<<d;
	d=b;
	std::cout<<"d: "<<d;

	a+=b;
	std::cout<<"a: "<<a;

	c=a+b;
	std::cout<<"c=a+b:"<<c;
	std::cout<<"a+b"<<a+b;
	///@alex@effectivecode:上面这两行相比，第二行的效率更加高，看下面的例子：
	/*
	 *
String temp(s1 + s2);
return temp; 
和
return String(s1 + s2); 

这个时候，第一个会先将temp创建出来，再拷贝给函数外部在栈上创建的临时变量；而第二个则是直接用string(s1+s2)去拷贝初始化外部栈上的临时变量，比较有效率
	 *
	 */

	c[0]=1000;c[1]=1001;c[2]=1002;c[3]=1003;
	std::cout<<"c: "<<c;
	//std::cout<<c.x<<std::endl;///@alex:上面一句同样是对x引用，是可以的；而这一句直接访问x，却不行，原因在于：引用类似于指针，是别名的意思，那么可以认为在operator[]中直接返回了对x的指针或别名引用，那么由于private的限制仅仅在语言层面（即没有在类外部出现x这个字符就可以），那么对于这个已有的指针或引用，对其进行操作就不会涉及任何private会关注的东西或者token（即，private检查是发生在编译阶段，只要不是发现token x是出现在外部类的文件的token列表中既可，而这儿的指针或引用完全是运行时的状况，不会发生任何问题），两者运行有效期不同，自然效果也就不同了。注意private完全是编译器，而引用或指针则不然。


	c(0)=2000;c(1)=2001;c(2)=2002;c(3)=2003;
	std::cout<<"c: "<<c;

	std::cout<<"d: "<<d;
	std::cout<<"((++d).getx()): "<<(++d).getx()<<std::endl<< ++d << d++ ;///@alex:debug this,error.可能是<<的运算优先级导致的问题，和我原先的想法是反的
	///@alex:解析开来是
	//假设定义是std::ostream & operator<< (std::ostream & cout_, const rect & data_) { cout_<<data_.xxx<<std::endl; return cout_;); 那么实际就是：
	//operator<<( operator<<( operator<<( ( ( operator<<( ( operator<<(std::cout, "((++d).getx()): ") ) , (++d).getx() ) ), std::endl), ++d) ) , d++);
	//也就是说，会一个一个的调用压栈，不过由于默认是最后一个参数向最前一个参数的顺序压栈，所以，会一个一个的从d++，++d，std::endl，(++d).getx()，"((++d).getx()): "来计算参数，也就是说，先计算d++，将结果压栈，然后计算前一个参数，计算的时候发现是递归，继续压栈调用递归函数，这个时候，函数的最后的参数先被计算，为++d，并压栈，这样顺序向前，直到里面的"((++d).getx()): ")压栈完，发现再里面的是一个不用递归的参数了，就直接再压栈最里面的参数std::cout，然后，在其上调用operator<<，调用之后，意味着，和表现出来的一样，"((++d).getx()): "最先被输出，然后一层一层解开递归调用，输出之前压栈计算出来的值，也就是说，先从右到左的计算出值并压栈，然后再从左到右的输出，这个时候，表现出的就是先输出了大的，再输出小的这种奇怪的现象了。
	//注意了,在输出到++d的时候,由于压栈的时候,压入的是对于d的实际的引用,那么这个时候,输出会去取的值就是实际的引用针对的值,那么这个时候输出的就是实际的值(即,4,5,6,7),而不是中间的值(3,4,5,6),因为这个中间的调用是传引用调用.最后一次才是传值引用,那么就有了最后一次输出为初始值的状况
	//这一句输出的情况是:
	//$((++d).getx()): 4
	//$x is:4. y is:5. w is:6. h is:7.
	//$x is:1. y is:2. w is:3. h is:4.
	//如上分析即可
	std::cout<<"d.getx(): "<<d.getx()<<std::endl;

	return 0;
}

///@alex:test

//vs2008 express command line:cl tmp_var.cpp /Zi /EHsc
//win32 mingw g++ (gcc version 3.4.5 (mingw-vista special r3)): g++ -g tmp_var.cpp -o tmp_var.exe
//vs2008 express command line:cl tmp_var.cpp /Zi /EHsc /Za /Wall 这样可以去除cl对c++的非标准扩展

/*
 *
///@alex:!! @reference:http://www.cppblog.com/fwxjj/archive/2009/09/24/97177.html
0. 局部变量 vs 临时变量
   void swap (int&a, int &b)
{
int tmp=a;
a=b;
b=tmp;      
}
上面这段程序里的tmp变量实际上是局部变量而不是临时变量。临时变量有两个特征：1.invisiable，在程序代码中没有显式出现 2 没有名字 non-named。
出现临时变量/对象的场合
1.函数的返回值
2. 参数传递
    2,1 值传递 by -value
    2.2 采用 const-reference时,且需要转换时，例如 double d; const int &ref=d;
          实际上，编译器作了一次变化 int tmp=d; const int & ref=tmp;
3. ++ --的后置运算符。它们总是生成一个临时对象tmp=原对象，对原对象进行操作。但返回tmp
4. 对象间的隐式转换，例如 string str； str=“abc”;
 * 
 *
 *
*/

